package dkbta.util.time;

import java.util.Calendar;

/**
 * Represents a time interval (i.e. a pair of start and end times)
 * 
 * @author Uri Kanonov
 */
public class TimeInterval implements Comparable<TimeInterval>{
	private long _startTime;

	private long _endTime;

	public TimeInterval(long startTime, long endTime){
		_startTime = startTime;
		_endTime = endTime;

		if ((startTime < 0) || (endTime < 0) || (endTime < startTime))
			throw new IllegalArgumentException("TimeInterval can't be created: " + this);
	}

	@Override
	public boolean equals(Object o){
		if (!(o instanceof TimeInterval)){
			return false;
		}
		TimeInterval other = (TimeInterval)o;

		return other._startTime == _startTime && other._endTime == _endTime;
	}

	@Override
	public String toString(){
//		return "[" + _startTime + ", " + _endTime + "]";
//		return "[" + TimeUtils.timeOnly(_startTime) + ", " + TimeUtils.timeOnly(_endTime) + "]";
		return TimeUtils.timeAndDate(_startTime) + "," + TimeUtils.timeAndDate(_endTime);
	}

	/**
	 * Returns the start time
	 * 
	 * @return The start time
	 */
	public long getStartTime(){
		return _startTime;
	}

	/**
	 * Returns the start time as a calendar
	 * 
	 * @return The start time as a calendar
	 */
	public Calendar getStartTimeAsCalendar(){
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(_startTime);
		return cal;
	}

	/**
	 * Returns the end time
	 * 
	 * @return The end time
	 */
	public long getEndTime(){
		return _endTime;
	}

	public void setStartTime(long startTime){
		_startTime = startTime;
	}

	public void setEndTime(long endTime){
		_endTime = endTime;
	}

	/**
	 * Returns the end time as a calendar
	 * 
	 * @return The end time as a calendar
	 */
	public Calendar getEndTimeAsCalendar(){
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(_endTime);
		return cal;
	}

	public long getDuration(){
		return _endTime - _startTime;
	}

	/**
	 * Whether this interval ends before the other interval starts
	 * 
	 * @param another The other interval
	 * @return Whether this interval ends before the other interval starts
	 */
	public boolean isBefore(TimeInterval another){
		checkIsNotNull(another);
		return _endTime < another._startTime;
	}

	/**
	 * Whether this interval starts after the other interval ends
	 * 
	 * @param another The other interval
	 * @return Whether this interval starts after the other interval ends
	 */
	public boolean isAfter(TimeInterval another){
		checkIsNotNull(another);
		return _startTime > another._endTime;
	}

	/**
	 * Whether this interval contains (open range) the other interval
	 * 
	 * @param another The other interval
	 * @return Whether this interval contains (open range) the other interval
	 */
	public boolean isDuring(TimeInterval another){
		checkIsNotNull(another);
		return ((_startTime < another._startTime) && (_endTime > another._endTime));
	}

	/**
	 * Whether this interval contains (closed range) the other interval
	 * 
	 * @param another The other interval
	 * @return Whether this interval contains (closed range) the other interval
	 */
	public boolean isContainedIn(TimeInterval another){
		checkIsNotNull(another);
		return ((_startTime <= another._startTime) && (_endTime >= another._endTime));
	}

	/**
	 * Returns the overlap of this interval and the other interval
	 * 
	 * @param other The other interval
	 * @return The overlap of this and other
	 */
	public TimeInterval getOverlap(TimeInterval other){
		if (other == null){
			return null;
		}

		long s1 = _startTime;
		long e1 = _endTime;
		long s2 = other._startTime;
		long e2 = other._endTime;

		if (e1 >= s2 && e2 >= e1){
			return new TimeInterval(Math.max(s1, s2), e1);
		}else if (e2 >= s1 && e1 >= e2){
			return new TimeInterval(Math.max(s1, s2), e2);
		}else{
			return null;
		}
	}

	/**
	 * Checks that the given time interval isn't null, throws an IllegalArgumentException
	 * otherwise
	 * 
	 * @param timeInterval The interval to be checked
	 * @throws IllegalArgumentException If timeInterval is null
	 */
	private void checkIsNotNull(TimeInterval timeInterval){
		if (timeInterval == null)
			throw new IllegalArgumentException("TimeInterval can't be null");
	}

	/**
	 * Compares this interval with another according to start time
	 * 
	 * @param o The time interval to be compared
	 * @return A negative integer, zero, or a positive integer as this interval's start
	 *         time is earlier than, equal to, or later than that of the other time
	 *         interval.
	 */
	public int compareTo(TimeInterval o){
		if (_startTime > o._startTime){
			return 1;
		}

		if (_startTime < o._startTime){
			return -1;
		}

		return 0;
	}

	/**
	 * Turns this time interval in the union of itself and other. In other words:
	 * 
	 * <pre>
	 * this.start = min(this.start, other.start);
	 * this.end = max(this.end, other.end);
	 * </pre>
	 * 
	 * @param other The time interval to be united with
	 */
	public void uniteWith(TimeInterval other){
		_startTime = Math.min(_startTime, other._startTime);
		_endTime = Math.max(_endTime, other._endTime);
	}
}